﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.CodeDom;
using System.IO;

namespace nGSO
{
    /// <summary>
    /// For dynamic math function evaluation. Reads definition from app.config, created dynamic method and type
    /// </summary>
    public class DynamicFunctionGenerator
    {
        public static Assembly CompileInMemory(string code)
        {
            CSharpCodeProvider provider = new CSharpCodeProvider();

            CompilerParameters options = new CompilerParameters();
            options.IncludeDebugInformation = false;
            options.GenerateExecutable = false;
            options.GenerateInMemory = true;

            options.ReferencedAssemblies.Add("nGSO.dll");

            CompilerResults results = provider.CompileAssemblyFromSource(options, code);
            provider.Dispose();

            Assembly generatedAssembly = null;
            if (results.Errors.Count == 0)
            {
                generatedAssembly = results.CompiledAssembly;
            }

            return generatedAssembly;
        }

        public static CodeCompileUnit CreateExecutionClass(string typeNamespace,
                                                   string typeName,
                                                   string scriptBody)
        {
            CodeCompileUnit ccu = new CodeCompileUnit();
            
            CodeNamespace cns = new CodeNamespace(typeNamespace);
            cns.Imports.Add(new CodeNamespaceImport("System"));
            ccu.Namespaces.Add(cns);

            CodeTypeDeclaration parentClass = new CodeTypeDeclaration(typeName);
            cns.Types.Add(parentClass);

            parentClass.BaseTypes.Add(typeof(nGSO.IMultimodalFun));

            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = "EvaluateMultimodalFunction";
            method.Attributes = MemberAttributes.Public;

            CodeParameterDeclarationExpression agent = new CodeParameterDeclarationExpression(typeof(Agent), "agent");
            method.Parameters.Add(agent); // fitness calculation can be depenedent from other Agent properties

            CodeParameterDeclarationExpression point = new CodeParameterDeclarationExpression(typeof(double[]), "loc");
            method.Parameters.Add(point);

            method.ReturnType = new CodeTypeReference(typeof(double));

            CodeSnippetStatement methodBody = new CodeSnippetStatement(scriptBody);
            method.Statements.Add(methodBody);
            parentClass.Members.Add(method);

            return ccu;
        }

        public static string GenerateCode(string typeNamespace, string typeName, string scriptBody)
        {
            // Call our prior method to create the CodeCompileUnit
            CodeCompileUnit ccu = CreateExecutionClass(typeNamespace,
                                                       typeName, scriptBody);

            CSharpCodeProvider provider = new CSharpCodeProvider();
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BlankLinesBetweenMembers = false;
            options.IndentString = "\t";

            StringWriter sw = new StringWriter();
            try
            {
                provider.GenerateCodeFromCompileUnit(ccu, sw, options);
                sw.Flush();
            }
            finally
            {
                sw.Close();
            }

            return sw.GetStringBuilder().ToString();
        }
    }
}
